package com.haruhi.ws.client;

import com.haruhi.event.Message;
import com.haruhi.event.MessageQueue;
import com.haruhi.event.MessageSource;
import com.haruhi.ws.EndpointAdapter;
import com.haruhi.ws.WsListener;

import javax.websocket.ClientEndpointConfig;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class AbstractClientEndpoint implements WsListener {

    private static final WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();

    private Session session;
    private URI uri;
    private Map<String,String> handshakeHeaders;
    private ClientEndpointConfig configurator;
    private long reconnectPeriod;
    private EndpointAdapter adapter;
    private AtomicBoolean connecting = new AtomicBoolean(false);

    public Session getSession() {
        return session;
    }

    public URI getUri() {
        return uri;
    }
    public Map<String, String> getHandshakeHeaders() {
        return handshakeHeaders;
    }
    public ClientEndpointConfig getConfigurator() {
        return configurator;
    }

    public long getReconnectPeriod() {
        return reconnectPeriod;
    }

    public EndpointAdapter getAdapter() {
        return adapter;
    }

    public AtomicBoolean getConnecting() {
        return connecting;
    }

    /**
     *
     * @param url
     * @param handshakeHeaders
     * @param reconnectPeriod 若发生断连 重连间隔时长 单位毫秒
     * @return
     * @throws URISyntaxException
     */
    public AbstractClientEndpoint(String url, Map<String,String> handshakeHeaders, long reconnectPeriod) throws URISyntaxException {
        this.uri = new URI(url);
        this.handshakeHeaders = handshakeHeaders;
        this.configurator = ClientEndpointConfig.Builder.create().configurator(new ClientEndpointConfig.Configurator(){
            @Override
            public void beforeRequest(Map<String, List<String>> headers) {
                if (handshakeHeaders != null && !handshakeHeaders.isEmpty()) {
                    handshakeHeaders.forEach((k,v) ->{
                        headers.put(k, Collections.singletonList(v));
                    });
                }

            }
        }).build();
        this.reconnectPeriod = reconnectPeriod;
        this.adapter = new EndpointAdapter(this);
    }

    /**
     *
     * @param autoReConnect 若连接失败 是否自动开启重连
     * @return 返回true表示连接成功
     */
    public synchronized boolean connect(boolean autoReConnect) {
        try {
            if(session == null){
                session = webSocketContainer.connectToServer(adapter, configurator,uri);
            }
            return true;
        } catch (Exception e){
            // 连接失败
            if(autoReConnect) {
                reConnect();
            }
            return false;
        }
    }


    /**
     * 重连
     * @return
     */
    public synchronized Future<Boolean> reConnect(){
        if(!connecting.get()){
            connecting.set(true);
            // 开始重连
            Callable<Boolean> callable = () -> {
                close();
                while (!Thread.currentThread().isInterrupted()) {
                    if (connect(false)) {
                        connecting.set(false);
                        return true;
                    } else {
                        try {
                            Thread.sleep(reconnectPeriod);
                        } catch (InterruptedException e) {
                            return false;
                        }
                    }
                }
                return false;
            };
            FutureTask<Boolean> reConnectFutureTask = new FutureTask<>(callable);
            Thread thread = new Thread(reConnectFutureTask);
            thread.start();

            return reConnectFutureTask;
        }

        // 直接返回false表示重连任务已经在执行中了
        FutureTask<Boolean> booleanFutureTask = new FutureTask<>(() -> false);
        Thread thread = new Thread(booleanFutureTask);
        thread.start();
        return booleanFutureTask;
    }
    public synchronized void close(){
        if (this.session != null) {
            if (this.session.isOpen()) {
                try {
                    this.session.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            this.session = null;
        }
    }

    @Override
    public void onOpen(Session session, EndpointConfig endpointConfig) {

    }

    @Override
    public void onMessage(Session session, String msg) {
        // 这里的消息都是远程服务端传来的 所以source是SERVER
        MessageQueue.offer(new Message(session,msg, MessageSource.SERVER));
    }

    @Override
    public void onError(Session session, Throwable throwable) {
        reConnect();
    }

    @Override
    public void onClose(Session session, CloseReason closeReason) {
        reConnect();
    }
}
